package cn.csg.iotplatform.module.system.controller.admin.auth;

import cn.csg.iotplatform.framework.common.enums.CommonStatusEnum;
import cn.csg.iotplatform.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.csg.iotplatform.framework.common.pojo.CommonResult;
import cn.csg.iotplatform.framework.datapermission.core.annotation.DataPermission;
import cn.csg.iotplatform.framework.operatelog.core.annotations.OperateLog;
import cn.csg.iotplatform.framework.security.config.SecurityProperties;
import cn.csg.iotplatform.framework.security.core.util.SecurityFrameworkUtils;
import cn.csg.iotplatform.module.system.controller.admin.auth.vo.*;
import cn.csg.iotplatform.module.system.convert.auth.AuthConvert;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.MenuDO;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.RoleDO;
import cn.csg.iotplatform.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.csg.iotplatform.module.system.dal.dataobject.user.AdminUserDO;
import cn.csg.iotplatform.module.system.dal.mysql.permission.MenuMapper;
import cn.csg.iotplatform.module.system.dal.mysql.permission.RoleMapper;
import cn.csg.iotplatform.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.csg.iotplatform.module.system.enums.ErrorCodeConstants;
import cn.csg.iotplatform.module.system.enums.logger.LoginLogTypeEnum;
import cn.csg.iotplatform.module.system.enums.user.UserIdEnum;
import cn.csg.iotplatform.module.system.service.auth.AdminAuthService;
import cn.csg.iotplatform.module.system.service.permission.MenuService;
import cn.csg.iotplatform.module.system.service.permission.PermissionService;
import cn.csg.iotplatform.module.system.service.permission.RoleService;
import cn.csg.iotplatform.module.system.service.user.AdminUserService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.aostarit.smcrypto.Sm2Utils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.csg.iotplatform.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csg.iotplatform.framework.common.pojo.CommonResult.success;
import static cn.csg.iotplatform.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.csg.iotplatform.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

@Tag(name = "管理后台 - 认证")
@RestController
@RequestMapping("/system/auth")
@Validated
@Slf4j
public class AuthController {

    @Resource
    private AdminAuthService authService;
    @Resource
    private AdminUserService userService;
    @Resource
    private MenuMapper menuMapper;
    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menuService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private SecurityProperties securityProperties;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Value("${yunjing.privateKey}")
    private String yujingPrivateKey;

    @PostMapping("/login")
    @PermitAll
    @Operation(summary = "使用账号密码登录")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
        return success(authService.login(reqVO));
    }

    @PostMapping("/login_yujing")
    @PermitAll
    @Operation(summary = "云景平台登录")
    @OperateLog(enable = false)
    public CommonResult<AuthLoginRespVO> loginYuJing(@RequestBody AuthLoginYuJingReqVO reqVO) {
        log.info("yunjing login->>params={}",reqVO);
        if(StrUtil.isBlank(reqVO.getEncyptData())){
            throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "token不能为空");
        }
        String result;
        try {
            result = new Sm2Utils().decryptToText(yujingPrivateKey, reqVO.getEncyptData());
        } catch (Exception e) {
            log.error("yunjing login->>解密失败",e);
            throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "token解析失败");
        }
        log.info("yunjing login->>解密结果={}",result);
        if(StrUtil.isBlank(result)) return null;
        AdminUserDO adminUserDO = userService.getUserByUsername(getAccount(result));
        return success(authService.createToken(adminUserDO,LoginLogTypeEnum.LOGIN_YUJING));
    }

    private String getAccount(String result){
        String[] resultArr = result.split("&");
        for(String str : resultArr){
            if(str.startsWith("account=")){
                return str.split("=")[1];
            }
        }
        return null;
    }

    @PostMapping("/token")
    @PermitAll
    @Operation(summary = "使用账号密码登录获取token")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> token(@RequestBody @Valid AuthTokenReqVO reqVO) {
        return success(authService.token(reqVO));
    }

    @PostMapping("/logout")
    @PermitAll
    @Operation(summary = "登出系统")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<Boolean> logout(HttpServletRequest request) {
        String token = SecurityFrameworkUtils.obtainAuthorization(request,
                securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
        if (StrUtil.isNotBlank(token)) {
            authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
        }
        return success(true);
    }

    @PostMapping("/check-token")
    @PermitAll
    @Operation(summary = "校验token并返回用户信息")
    @OperateLog(enable = false)
    public CommonResult<CheckTokenRespVO> checkToken(@RequestBody @Valid CheckTokenReqVO reqVO) {
        return success(authService.checkToken(reqVO));
    }

    @PostMapping("/refresh-token")
    @PermitAll
    @Operation(summary = "刷新令牌")
    @Parameter(name = "refreshToken", description = "刷新令牌", required = true)
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
        return success(authService.refreshToken(refreshToken));
    }

    @GetMapping("/get-permission-info")
    @Operation(summary = "获取登录用户的权限信息")
    public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
        // 1.1 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            return null;
        }

        // 1.2 获得角色列表
        Set<Long> roleIds = user.getId() == UserIdEnum.SUPERADMIN.getUserId() ? roleService.getRoleList().stream().map(RoleDO::getId).collect(Collectors.toSet()) : permissionService.getUserRoleIdListByUserId(getLoginUserId());
        if (CollUtil.isEmpty(roleIds)) {
            return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList()));
        }
        List<RoleDO> roles = roleService.getRoleList(roleIds);
        roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色

        // 1.3 获得菜单列表
        Set<Long> menuIds = user.getId() == UserIdEnum.SUPERADMIN.getUserId() ? convertSet(menuMapper.selectList(), MenuDO::getId) : permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
        List<MenuDO> menuList = Collections.emptyList();
        if(menuIds.size() > 0){
            menuList = menuService.getMenuList(menuIds);
            menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
        }

        if(CollUtil.isEmpty(menuList)) return CommonResult.error(ErrorCodeConstants.USER_NO_PERMISSION);

        // 2. 拼接结果返回
        return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
    }


    @PostMapping("/check-password")
    @PermitAll
    @Operation(summary = "校验密码", description = "校验密码")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<Integer> checkPassword(@RequestBody AuthLoginReqVO reqVO) {
        return success(authService.checkPassword(reqVO));
    }

}
